package com.sayimo.maker.commons.wx.pay;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jdom2.JDOMException;

import com.sayimo.framework.exception.DaoException;
import com.sayimo.framework.exception.ServiceException;
import com.sayimo.maker.commons.Constant;
import com.sayimo.maker.commons.utils.JSONUtil;
import com.sayimo.maker.commons.utils.PropertiesUtils;
import com.sayimo.maker.commons.utils.XMLUtil;

public class WXPay {

	private static Log logger = LogFactory.getLog(WXPay.class);
	static String WX_MCHID=PropertiesUtils.getValue("WX_MCHID");
	static String WX_APPID=PropertiesUtils.getValue("WX_APPID");

	/**
	 * 
	 * @MethodName wxPayOrder
	 * @Description 微信预支付
	 * 
	 * @author chenpy
	 * @date 2016年1月9日 上午11:17:22
	 * @param <p>
	 *            _paramMap
	 * 
	 *            <pre>
	 *            payPrice 支付金额 ordersNo 订单号 goodsName 商品名称
	 * @param req
	 * @return Map
	 * @VERSION 1.0.0
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static Map wxPayOrder(Map _paramMap, HttpServletRequest req)
			throws IllegalAccessException, InvocationTargetException, ServiceException {
		Map resultMap = new HashMap();

		try {
			logger.info("tradeType==APPPPPPPPPPPPPPPP");
			logger.info("paramMap = " + JSONUtil.writeValueAsString(_paramMap));
			Map<String, String> requestMap = getRequestXml(_paramMap, req);
			String requestXML = requestMap.get("requestXML");
			logger.info("requestXML = " + requestXML);
			String result = getUnifiedorder(requestXML);
			logger.info("result = " + result);
			/* 解析微信返回的信息，以Map形式存储便于取值 */
			Map<String, String> map = XMLUtil.doXMLParse(result);
			logger.info("XMLUTIL.DOXMLPARSE = " + JSONUtil.writeValueAsString(map));

			final SortedMap<Object, Object> params = new TreeMap<Object, Object>();
			if ("SUCCESS".equals(map.get("result_code"))) {
				params.put("appid", WX_APPID);
				params.put("partnerid", WX_MCHID);
				Long timeStamp = new Date().getTime();
				params.put("timestamp", Long.toString(timeStamp / 1000));
				params.put("noncestr", WXUtil.getNonceStr());
				/*
				 * 获取预支付单号prepay_id后，需要将它参与签名。
				 * 微信支付最新接口中，要求package的值的固定格式为prepay_id=...
				 */
				params.put("package", "Sign=WXPay");
				params.put("prepayid", map.get("prepay_id"));
				
				if("WAP".equals(_paramMap.get("applyType"))){
					//H5发起支付链接地址
					params.put("mweburl", map.get("mweb_url"));
				}
				
				/**
				 * 获取预支付prepay_id之后，再次进行签名，参与签名需要的参数有：appId、timeStamp、nonceStr、
				 * package、signType. 主意上面参数名称的大小写.
				 * 该签名用于前端js中WeixinJSBridge.invoke中的paySign的参数值
				 */
				String paySign = WXUtil.createSign("UTF-8", params);

				params.put("paySign", paySign);

				params.put("sign", paySign);
				/*
				 * 预支付单号，前端ajax回调获取。由于js中package为关键字，所以，这里使用packageValue作为key。
				 */
				// params.put("packageValue", "prepAy_id=" +
				// map.get("prepay_id"));

				/** 付款成功后，微信会同步请求我们自定义的成功通知页面，通知用户支付成功 */
				params.put("sendUrl", "");
				/** 订单批次号 */
				String ordersNo = (String) _paramMap.get("ordersNo");
				params.put("out_trade_no", ordersNo);
				 
				/* 获取用户的微信客户端版本号，用于前端支付之前进行版本判断，微信版本低于5.0无法使用微信支付 */
				String userAgent = req.getHeader("user-agent");
				logger.info("userAgent = " + userAgent);
				if (userAgent.indexOf("MicroMessenger") == -1) {
					params.put("agent", 0);
				} else {
					char agent = userAgent.charAt(userAgent.indexOf("MicroMessenger") + 15);
					params.put("agent", new String(new char[] { agent }));
				}
				//联盟商扫码支付交易记录中的用户记录单号
				if(_paramMap.get("consumerOrdersNo")!=null){
					params.put("consumerOrdersNo", (String) _paramMap.get("consumerOrdersNo"));
				}

			} else { // 订单支付失败
				resultMap.put(Constant.STATUS, Constant.STATUS_ERROR);
				resultMap.put(Constant.ERROR_CODE, Constant.PAY_ORDER_FAIL);

				return resultMap;
			}

			logger.info("params = " + JSONUtil.writeValueAsString(params));
			resultMap.put(Constant.STATUS, Constant.STATUS_SUCCESS);
			resultMap.put(Constant.DATA, params);
			logger.info("resultMap = " + JSONUtil.writeValueAsString(resultMap));

			// JMSMessage.sendMessage(requestMap.get("ordersNo"), 1);
			return resultMap;

		} catch (Exception e) {
			e.printStackTrace();
			resultMap.put(Constant.STATUS, Constant.STATUS_ERROR);
			resultMap.put(Constant.ERROR_CODE, Constant.SYSTEM_ERROR);
		}
		return resultMap;
	}

	/**
	 * 
	 * @MethodName getUnifiedorder
	 * @Description 从腾讯拉取预支付号
	 * @author chenpy
	 * @date 2016年1月9日 上午11:20:57
	 * @param
	 * @return String
	 * @throws Exception
	 * @VERSION 1.0.0
	 */
	public static String getUnifiedorder(String requestXML) throws Exception {
		String result = null;
		HttpClient client = new HttpClient();
		PostMethod myPost = new PostMethod("https://api.mch.weixin.qq.com/pay/unifiedorder");
		client.getParams().setSoTimeout(300 * 1000);
		try {
			myPost.setRequestEntity(new StringRequestEntity(requestXML, "text/xml", "utf-8"));
			int statusCode = client.executeMethod(myPost);
			if (statusCode == HttpStatus.SC_OK) {
				BufferedInputStream bis = new BufferedInputStream(myPost.getResponseBodyAsStream());
				byte[] bytes = new byte[1024];
				ByteArrayOutputStream bos = new ByteArrayOutputStream();
				int count = 0;
				while ((count = bis.read(bytes)) != -1) {
					bos.write(bytes, 0, count);
				}
				byte[] strByte = bos.toByteArray();
				result = new String(strByte, 0, strByte.length, "utf-8");
				logger.info("result = " + result);
				bos.close();
				bis.close();
			}
		} catch (Exception e) {
			throw new Exception(e);
		} finally {
			myPost.releaseConnection();
			client.getHttpConnectionManager().closeIdleConnections(0);
		}

		return result;
	}

	/**
	 * 
	 * @MethodName getRequestXml
	 * @Description 生成获取微信支付批次号的请求参数
	 * 
	 * @author chenpy
	 * @date 2016年1月9日 上午11:28:08
	 * @param <p>
	 *            _paramMap
	 * 
	 *            <pre>
	 *            payPrice 支付金额 ordersNo 订单号 goodsName 商品名称
	 * 
	 * @return String
	 * @throws DaoException
	 * @throws UnsupportedEncodingException 
	 * @VERSION 1.0.0
	 */
	@SuppressWarnings({ "rawtypes" })
	public static Map<String, String> getRequestXml(Map _paramMap, HttpServletRequest req)
			throws IllegalAccessException, InvocationTargetException, ServiceException, DaoException, UnsupportedEncodingException {
		Map<String, String> resultMap = new HashMap<>();
		String requestXML = null;
		String path = req.getContextPath();
		Double totalPrice = (Double) _paramMap.get("payPrice");
		/** 总金额(分为单位) */
		int total = (int) (totalPrice * 100);

		SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
		/** 公众号APPID */
		parameters.put("appid", WX_APPID);
		/** 商户号 */
		parameters.put("mch_id", WX_MCHID); // 1246536001
		/** 随机字符串 */
		parameters.put("nonce_str", WXUtil.getNonceStr());

		/** 订单批次号 */
		String orderIdJoin = (String) _paramMap.get("ordersNo");
		parameters.put("out_trade_no", orderIdJoin);

		/* 订单金额以分为单位，只能为整数 */
		parameters.put("total_fee", total);

//		if("APP".equals(_paramMap.get("applyType"))){
//			/* 支付方式为APP支付 */
//			parameters.put("trade_type", "APP");
//		}else 
			if("WAP".equals(_paramMap.get("applyType"))){
			/* 支付方式为H5支付 */
			parameters.put("trade_type", "MWEB");
		}else{
			/* 支付方式为APP支付 */
			parameters.put("trade_type", "APP");
		}
		

		/* 支付回调地址 */
		parameters.put("notify_url", PropertiesUtils.getValue("SERVER_URL") + req.getContextPath()+"/orders/callbackalipay");
		/* 客户端本地ip */
		logger.info("***********************spbill_create_ip="+req.getHeader("X-Real-IP"));
		parameters.put("spbill_create_ip", req.getHeader("X-Real-IP"));
		/* 商品名称 */
		String goodsName = (String) _paramMap.get("goodsName");
		parameters.put("body", goodsName);
		
		// 微信回掉参数
        {
	        Map<String, Object> passBackMap = new HashMap<>();
	        Set<String> keys = _paramMap.keySet();
	        for (String key : keys) {
				if (!key.startsWith("_")) {
					continue;
				}						
				passBackMap.put(key, _paramMap.get(key));
			}	        
	        if (MapUtils.isNotEmpty(passBackMap)) {
	        	String passBackParams = JSONUtil.writeValueAsString(passBackMap);
		        logger.info("passBackParams = " + passBackParams);
		        
		      //公用回传参数，如果请求时传递了该参数，则返回给商户时会回传该参数。微信不需要UrlEncode，不然长度会超出， 支付宝会在异步通知时将该参数原样返回。本参数必须进行UrlEncode之后才可以发送给支付宝
		        parameters.put("attach",passBackParams);  
			}
        }

		logger.info("parameters = " + JSONUtil.writeValueAsString(parameters));
		/** MD5进行签名，必须为UTF-8编码，注意上面几个参数名称的大小写 */
		String sign = WXUtil.createSign("UTF-8", parameters);

		// String sign = ret.get("signature");
		parameters.put("sign", sign);
		
		/* 生成xml结构的数据，用于统一下单接口的请求 */
		requestXML = WXUtil.getRequestXml(parameters);
		logger.info("requestXML：" + requestXML);
		resultMap.put("requestXML", requestXML);
		resultMap.put("ordersNo", orderIdJoin);
		/* 开始请求统一下单接口，获取预支付prepay_id */
		return resultMap;
	}

	/**
	 * @Package com.sayimo.school.mall.module.order.service.impl
	 * @Author 龚益君
	 * @MethodName:orderQuery
	 * @Description:查询微信已支付，本地数据库依然未支付的订单
	 * @param ordersList
	 * @return
	 * @throws JDOMException
	 * @throws IOException
	 * @Date 2016年9月26日,下午3:03:19
	 */
	public static boolean orderQuery(String ordersNo) throws Exception {
		System.out.println(ordersNo);
		String nonceStr = WXUtil.getNonceStr();
		SortedMap<Object, Object> sortedMap = new TreeMap<Object, Object>();
		sortedMap.put("appid", WX_APPID);
		sortedMap.put("mch_id", WX_MCHID);
		sortedMap.put("out_trade_no", ordersNo);
		sortedMap.put("nonce_str", nonceStr);
		String sign = WXUtil.createSign("UTF-8", sortedMap);
		sortedMap = new TreeMap<Object, Object>();
		sortedMap.put("appid", WX_APPID);
		sortedMap.put("mch_id", WX_MCHID);
		sortedMap.put("out_trade_no", ordersNo);
		sortedMap.put("nonce_str", nonceStr);
		sortedMap.put("sign", sign);
		String requestXML = WXUtil.getRequestXml(sortedMap);
		HttpClient client = new HttpClient();
		PostMethod myPost = new PostMethod("https://api.mch.weixin.qq.com/pay/orderquery");
		client.getParams().setSoTimeout(300 * 1000);
		String result = null;
		try {
			myPost.setRequestEntity(new StringRequestEntity(requestXML, "text/xml", "utf-8"));
			int statusCode = client.executeMethod(myPost);
			if (statusCode == HttpStatus.SC_OK) {
				BufferedInputStream bis = new BufferedInputStream(myPost.getResponseBodyAsStream());
				byte[] bytes = new byte[1024];
				ByteArrayOutputStream bos = new ByteArrayOutputStream();
				int count = 0;
				while ((count = bis.read(bytes)) != -1) {
					bos.write(bytes, 0, count);
				}

				byte[] strByte = bos.toByteArray();
				result = new String(strByte, 0, strByte.length, "utf-8");
				bos.close();
				bis.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		/* 需要释放掉、关闭连接 */
		myPost.releaseConnection();
		client.getHttpConnectionManager().closeIdleConnections(0);
		if (!StringUtils.isEmpty(result)) {
			Map<String, String> map = XMLUtil.doXMLParse(result);

			if (map.get("return_code") != null && map.get("return_code").equals("SUCCESS")
					&& map.get("result_code") != null && map.get("result_code").equals("SUCCESS")
					&& map.get("trade_state") != null && map.get("trade_state").equals("SUCCESS")) { // 支付成功.但是订单状态还是待支付

				return true;
			}
		}
		return false;

	}

	/**
	 * @方法名称:payRefund
	 * @内容摘要: ＜微信退款＞
	 * @param out_refund_no 商户订单号
	 * @param total_fee 总金额
	 * @param refund_fee 退款金额
	 * @param op_user_id 操作人
	 * @return String
	 * @throws IOException
	 * @throws JDOMException
	 * @author 龚益君
	 * @创建日期:2017年3月06日-上午11:07:04
	 */
	public static String wxPayRefundRequest(Map<String, Object> wxPayRefundMap) throws Exception {
		String nonceStr = WXUtil.getNonceStr();
		String ordersNo = wxPayRefundMap.get("ordersNo").toString();
		String total_fee = wxPayRefundMap.get("total_fee").toString();
		String refund_fee = wxPayRefundMap.get("refund_fee").toString();
		SortedMap<Object, Object> sortedMap = new TreeMap<Object, Object>();
		sortedMap.put("appid", WX_APPID);
		sortedMap.put("mch_id", WX_MCHID);
		sortedMap.put("out_trade_no", ordersNo);
		sortedMap.put("nonce_str", nonceStr);
		sortedMap.put("out_refund_no", ordersNo);
		sortedMap.put("total_fee", total_fee);//总金额
		sortedMap.put("refund_fee", refund_fee);//退款金额
		sortedMap.put("op_user_id", PropertiesUtils.getValue("WX_MCHID"));//操作人，一般为商户ID
		String sign = WXUtil.createSign("UTF-8", sortedMap);
		sortedMap.put("sign", sign);
		String requestXML = WXUtil.getRequestXml(sortedMap);
		HttpClient client = new HttpClient();
		PostMethod myPost = new PostMethod("https://api.mch.weixin.qq.com/secapi/pay/refund");
		client.getParams().setSoTimeout(300 * 1000);
		String result = null;
		try {
			myPost.setRequestEntity(new StringRequestEntity(requestXML, "text/xml", "utf-8"));
			int statusCode = client.executeMethod(myPost);
			if (statusCode == HttpStatus.SC_OK) {
				BufferedInputStream bis = new BufferedInputStream(myPost.getResponseBodyAsStream());
				byte[] bytes = new byte[1024];
				ByteArrayOutputStream bos = new ByteArrayOutputStream();
				int count = 0;
				while ((count = bis.read(bytes)) != -1) {
					bos.write(bytes, 0, count);
				}
				byte[] strByte = bos.toByteArray();
				result = new String(strByte, 0, strByte.length, "utf-8");
				bos.close();
				bis.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		/* 需要释放掉、关闭连接 */
		myPost.releaseConnection();
		client.getHttpConnectionManager().closeIdleConnections(0);
		if (!StringUtils.isEmpty(result)) {
			Map<String, String> map = XMLUtil.doXMLParse(result);
			if (map.get("return_code") != null && map.get("return_code").equals("SUCCESS")
					&& map.get("result_code") != null && map.get("result_code").equals("SUCCESS")) { // 退款成功
				return Constant.SUCCESS;
			}
		}
		return Constant.ERROR;
	}
	
	
	 /**
	 * @Package com.sayimo.maker.commons.wx.pay
	 * @Author 龚益君
	 * @MethodName:callbackWXPay
	 * @Description:微信回调
	 * @param request
	 * @return
	 * @throws Exception
	 * @Date 2017年3月21日,上午11:52:29
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static Map<String, Object> callbackWXPay(Map map)throws Exception {
		Map<String,Object> resultMap = new HashMap<String,Object>();
		try {
	        Map<String, String> callbackParamMap = map;
	        logger.info("callbackParamMap = " + callbackParamMap);
	        logger.info("callbackParamMap = " + JSONUtil.writeValueAsString(callbackParamMap));  
	        logger.info("result_code = " + callbackParamMap.get("result_code") + ", return_code = " + callbackParamMap.get("return_code"));
	        if(!"SUCCESS".equals(callbackParamMap.get("result_code"))){
	        	resultMap.put(Constant.STATUS, Constant.STATUS_ERROR);
	        	return resultMap;
	        }
	        
	        logger.info("attach = " + callbackParamMap.get("attach"));
	        if (callbackParamMap.containsKey("attach")) {
	        	String passbackParams = callbackParamMap.get("attach");
	        	passbackParams = URLDecoder.decode(passbackParams, "UTF-8");
	        	logger.info("passbackParams = " + passbackParams);
	        	Map passBackMap = JSONUtil.readValueToMap(passbackParams);
	        	resultMap.putAll(passBackMap);
			}
	        
	        logger.info("callbackParamMap = " + callbackParamMap);
	        logger.info("callbackParamMap = " + JSONUtil.writeValueAsString(callbackParamMap));
	        
	        String appId = callbackParamMap.get("app_id"), seller_id = callbackParamMap.get("mch_id"),
	        		_payPrice = String.valueOf(resultMap.get("_payPrice"));
	        		
	        
	        Double jPayPrice = Double.valueOf(_payPrice)*100D,
	        		total_fee = Double.valueOf(String.valueOf(callbackParamMap.get("total_fee")));
	        logger.info("total_amount = " + total_fee + ", _payPrice = " + _payPrice);
	        
	        // 比对金额
	        if (!resultMap.containsKey("_payPrice") || jPayPrice.compareTo(total_fee) != 0) {
	        	resultMap.put(Constant.STATUS, Constant.STATUS_ERROR);
	        	return resultMap;
			}
	        
	        // 不是微信回调（参数被篡改）
	        if(!WX_APPID.equals(appId) || !WX_MCHID.equals(seller_id)){
	        	resultMap.put(Constant.STATUS, Constant.STATUS_ERROR);
	        	return resultMap;
	       	}
	        
	        resultMap.put(Constant.STATUS, Constant.STATUS_SUCCESS);
	        resultMap.put("ordersNo", callbackParamMap.get("out_trade_no")); // 订单号
	        logger.info("resultMap = " + resultMap);
	        return resultMap;
		}catch (Exception e) {
			e.printStackTrace();
			throw new ServiceException(e);
		}
	}
}
